home *** CD-ROM | disk | FTP | other *** search
/ PC User 2003 January / Disc 1 / PCU0103CD1.iso / entertn / demos / files / aomtrial.exe / AOM / AI / SCN24P3.XS < prev    next >
Encoding:
Text File  |  2002-09-23  |  24.4 KB  |  749 lines

  1. //==============================================================================
  2. // Scn24p3: AI Scenario Script for scenario 24 player 3
  3. //==============================================================================
  4. /*
  5.    AI owner:  Dave Leary
  6.    Scenario owner: Dave Leary
  7.  
  8.     The AI for Circe and friends in scenario 24.
  9.  
  10.                                 *** DIFFICULTY LEVEL NOTES ***
  11.  
  12.    Easy level - Even more towers removed.  Guards near Circe's fortress removed.
  13.     Fewer researches.  No archers included in attacks.
  14.  
  15.    Moderate level - Base level.
  16.  
  17.    Difficult level - Towers around Circe's fortress.  Additional researches.
  18.     Petroboli maintained and added to infantry attack groups.
  19.  
  20.    Nightmare - Lots of towers around Circe's fortress.  Additional researches.
  21.     Petroboli maintained and added to infantry attack groups.
  22. */
  23. //==============================================================================
  24. // Difficulty Level check predeclared.
  25. int difflevel=-1;        
  26. //==============================================================================
  27. // Set Town Location
  28. //==============================================================================
  29. void setTownLocation(void)
  30. {
  31.    //Look for the "Town Location" marker.
  32.    kbSetTownLocation(kbGetBlockPosition("20871"));
  33. }
  34.  
  35. //==============================================================================
  36. // Basic startup
  37. //==============================================================================
  38. void miscStartup(void)
  39. {
  40.     // Get the difficulty level.
  41.     difflevel=aiGetWorldDifficulty();
  42.  
  43.    //Startup message(s).
  44.    aiEcho("");
  45.    aiEcho("");
  46.    aiEcho("Scn24P3 AI Start, filename='"+cFilename+"'.");
  47.    //Spit out the map size.
  48.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  49.     aiEcho("Difficulty Level="+difflevel+".");
  50.  
  51.    //Cheat like a bastard.  Once only, though.
  52.    kbLookAtAllUnitsOnMap();
  53.    //Calculate some areas.
  54.    kbAreaCalculate(1200.0);
  55.    //Set our town location.
  56.    setTownLocation();
  57.     //Reset random seed
  58.     aiRandSetSeed();
  59.  
  60.     //Allocate all resources to the root escrow by setting percentage of military/economy to 0.
  61.     kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 );
  62.     kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 );
  63.  
  64.    //Allocate all resources to the root escrow.
  65.    kbEscrowAllocateCurrentResources();
  66.  
  67.     // Drop the AI attack response distance for this player to 3 meters.  We'll up it later.
  68.     aiSetAttackResponseDistance(3.0);
  69. }
  70.  
  71. //==============================================================================
  72. //==============================================================================
  73. // Attack stuff.
  74. //==============================================================================
  75. //==============================================================================
  76. //Shared variables.
  77. int numberAttacks=0;
  78. int attackPlayerID=-1;
  79.  
  80. //TODO: Decide how to rep attack group size.
  81. int attackMinimumGroupSize=4;
  82. int attackMaximumGroupSize=6;
  83.  
  84. //Attack 1 vars.
  85. int attackPlan1ID=-1;
  86. int maintainPlan1ID=-1;
  87.  
  88. //Attack 2 vars.
  89. int attackPlan2ID=-1;
  90. int maintainPlan2ID=-1;
  91.  
  92. // Route and path vars
  93. int attackRoute1ID=-1;
  94. int attackPath1ID=-1;
  95. int attackRoute2ID=-1;
  96. int attackPath2ID=-1;
  97.  
  98. // Unit type(s) 
  99. int attackerUnitTypeID1=cUnitTypeHypaspist;
  100. int attackerUnitTypeID2=cUnitTypeHippikon;
  101. int attackerUnitTypeID3=cUnitTypeToxotes;
  102.  
  103. int siegeUnitTypeID1=cUnitTypePetrobolos;
  104.  
  105. //=========================================================================================
  106. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  107. //=========================================================================================
  108. bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 )
  109. {
  110.    if ( queryID == -1)
  111.    {
  112.       return(false);
  113.    }
  114.  
  115.    if (player != -1)
  116.       kbUnitQuerySetPlayerID(queryID, player);
  117.    
  118.    if (unitType != -1)
  119.       kbUnitQuerySetUnitType(queryID, unitType);
  120.  
  121.    if (action != -1)
  122.       kbUnitQuerySetActionType(queryID, action);
  123.  
  124.    if (state != -1)
  125.       kbUnitQuerySetState(queryID, state);
  126.  
  127.    if (center != vector(-1,-1,-1))
  128.    {
  129.       kbUnitQuerySetPosition(queryID, center);
  130.       if (sort == true)
  131.          kbUnitQuerySetAscendingSort(queryID, true);
  132.       if (radius != -1)
  133.          kbUnitQuerySetMaximumDistance(queryID, radius);
  134.    }
  135.    return(true);
  136. }
  137.  
  138. //==============================================================================
  139. // initAttack: Creates attack routes, etc. 
  140. //==============================================================================
  141. void initAttack(int playerID=-1)
  142. {
  143.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  144.    if (playerID != attackPlayerID)
  145.    {
  146.       //Reset the attack player ID.
  147.       attackPlayerID=-1;
  148.       //Destroy any previous attack plan.
  149.       aiPlanDestroy(attackPlan1ID);
  150.       attackPlan1ID=-1;
  151.       aiPlanDestroy(attackPlan2ID);
  152.       attackPlan2ID=-1;
  153.   
  154.       //Destroy our previous attack paths.
  155.       kbPathDestroy(attackPath1ID);
  156.       attackPath1ID=-1;
  157.       kbPathDestroy(attackPath2ID);
  158.       attackPath2ID=-1;
  159.  
  160.       //Destroy our previous attack routes.
  161.       attackRoute1ID=-1;
  162.       attackRoute2ID=-1;
  163.  
  164.       //Reset the number of attacks.
  165.       numberAttacks=0;
  166.    }
  167.  
  168.    //Save the player to attack.
  169.    attackPlayerID=playerID;
  170.  
  171.    vector gatherPointSouth=kbGetBlockPosition("20871");
  172.     vector gatherPointNorth=kbGetBlockPosition("20873");
  173.        
  174.     //Setup attack path 1 - for south group
  175.    attackPath1ID=kbPathCreate("Attack Path 1");
  176.    kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("20872"));
  177.     kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("20874"));
  178.     
  179.    //Create attack route 1.
  180.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPointSouth, kbGetBlockPosition("20875"));
  181.    
  182.     if (attackRoute1ID >= 0)
  183.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  184.  
  185.    //Setup attack path 2 - go right
  186.    attackPath2ID=kbPathCreate("Attack Path 2");
  187.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("20872"));
  188.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("20874"));
  189.    //Create attack route 2.
  190.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPointSouth, kbGetBlockPosition("20875"));
  191.    
  192.     if (attackRoute2ID >= 0)
  193.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  194. }
  195.  
  196. //==============================================================================
  197. // setupFirstAttack (hypaspists)
  198. //==============================================================================
  199. bool setupFirstAttack(int playerID=-1)
  200. {
  201.     // Get the difficulty level.
  202.     difflevel=aiGetWorldDifficulty();
  203.  
  204.      //Info.
  205.     aiEcho("Attacking Player "+playerID+".");
  206.  
  207.    //If the player to attack doesn't match, init the attack.
  208.    if (attackPlayerID != playerID)
  209.    {
  210.       initAttack(playerID);
  211.       if (attackPlayerID < 0)
  212.          return(false);
  213.    }
  214.  
  215.    //Create an attack plan.
  216.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  217.    if (newAttackPlanID < 0)
  218.       return(false);
  219.  
  220.    //Target player (required).  This must work.
  221.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  222.       return(false);
  223.  
  224.    //Gather point.
  225.     vector gatherPoint=kbGetBlockPosition("20871");
  226.  
  227.     //Set the target type.  This must work.
  228.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  229.       return(false);
  230.  
  231.    //Unit types to attack.
  232.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  233.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  234.  
  235.    //Attack route - always the southern route
  236.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  237.  
  238.    //Set the gather point and gather point distance.
  239.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  240.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0);
  241.  
  242.    //Set up the attack route usage pattern.
  243.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  244.    
  245.     //Add the unit types to the plan.  Base unit = hypaspists.  Also add a couple of toxotoes.
  246.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  247.  
  248.     if ( difflevel > 0 )
  249.     {
  250.         aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 2, 4);
  251.     }
  252.  
  253.     // When the difficulty level is greater than moderate, add petroboli if available.
  254.     if ( difflevel > 1 )
  255.     {
  256.         aiPlanAddUnitType(newAttackPlanID, siegeUnitTypeID1, 0, 2, 2);
  257.     }
  258.     
  259.    //Set the initial position.
  260.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  261.    //Plan requires all need units to work (can be false).
  262.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  263.    //Activate the plan.
  264.    aiPlanSetActive(newAttackPlanID);
  265.  
  266.    //Now, save the attack plan ID appropriately.
  267.    aiPlanSetOrphan(attackPlan1ID, true);
  268.    attackPlan1ID=newAttackPlanID;
  269.  
  270.    //Increment our overall number of attacks.
  271.    numberAttacks++;
  272. }
  273.  
  274. //==============================================================================
  275. // setupSecondAttack (hippikons)
  276. //==============================================================================
  277. bool setupSecondAttack(int playerID=-1)
  278. {
  279.     // Get the difficulty level.
  280.     difflevel=aiGetWorldDifficulty();
  281.  
  282.     //Info.
  283.     aiEcho("Attacking Player "+playerID+".");
  284.  
  285.    //If the player to attack doesn't match, init the attack.
  286.    if (attackPlayerID != playerID)
  287.    {
  288.       initAttack(playerID);
  289.       if (attackPlayerID < 0)
  290.          return(false);
  291.    }
  292.  
  293.    //Create an attack plan.
  294.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  295.    if (newAttackPlanID < 0)
  296.       return(false);
  297.  
  298.    //Target player (required).  This must work.
  299.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  300.       return(false);
  301.  
  302.    //Gather point.
  303.     vector gatherPoint=kbGetBlockPosition("20873");
  304.  
  305.     //Set the target type.  This must work.
  306.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  307.       return(false);
  308.  
  309.    //Unit types to attack.
  310.     if ( difflevel > 1 )
  311.     {
  312.        aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek);
  313.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  314.     }
  315.     else
  316.     {
  317.        aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding);
  318.         aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit);
  319.     }
  320.  
  321.    //Attack route - always the northern route
  322.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  323.  
  324.    //Set the gather point and gather point distance.
  325.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  326.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0);
  327.  
  328.    //Set up the attack route usage pattern.
  329.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  330.    
  331.     //Add the unit types to the plan.  Base unit = hippikons.
  332.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  333.     
  334.    //Set the initial position.
  335.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  336.    //Plan requires all need units to work (can be false).
  337.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  338.    //Activate the plan.
  339.    aiPlanSetActive(newAttackPlanID);
  340.  
  341.    //Now, save the attack plan ID appropriately.
  342.    aiPlanSetOrphan(attackPlan1ID, true);
  343.    attackPlan1ID=newAttackPlanID;
  344.  
  345.    //Increment our overall number of attacks.
  346.    numberAttacks++;
  347. }
  348.  
  349. //==============================================================================
  350. // Attack Generator 1 - base unit, hypaspist
  351. //==============================================================================
  352. rule attackGenerator1
  353.    minInterval 90
  354.    inactive
  355.    group AttackRules
  356.    runImmediately
  357. {
  358.    //See how many "idle" attack plans we have.  Don't create any more if we have
  359.    //idle plans.
  360.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  361.  
  362.    if (numberIdleAttackPlans > 0)
  363.       return;
  364.  
  365.    //If we have enough unassigned military units, create a new attack plan.
  366.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1);
  367.    aiEcho("There are "+numberAvailableUnits+" hypaspists available for a new attack.");
  368.    
  369.     if (numberAvailableUnits >= attackMinimumGroupSize)
  370.         setupFirstAttack(1);
  371. }
  372.  
  373. //==============================================================================
  374. // Attack Generator 2 - base unit, hippikons
  375. //==============================================================================
  376. rule attackGenerator2
  377.    minInterval 135
  378.    inactive
  379.    group AttackRules
  380.    runImmediately
  381. {
  382.    //See how many "idle" attack plans we have.  Don't create any more if we have
  383.    //idle plans.
  384.    int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  385.  
  386.    if (numberIdleAttackPlans > 0)
  387.       return;
  388.  
  389.    //If we have enough unassigned military units, create a new attack plan.
  390.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2);
  391.    aiEcho("There are "+numberAvailableUnits+" hippikons available for a new attack.");
  392.    
  393.     if (numberAvailableUnits >= attackMinimumGroupSize)
  394.         setupSecondAttack(1);
  395. }
  396.  
  397.  
  398. //==============================================================================
  399. // Attack enablers - enable attacks after initial timers expire
  400. //==============================================================================
  401. rule attack1Enabler
  402.    minInterval 120
  403.    inactive
  404.    group AttackRules
  405. {
  406.    xsEnableRule("attackGenerator1");
  407.    xsDisableSelf();
  408. }
  409.  
  410.  
  411. rule attack2Enabler
  412.    minInterval 150
  413.    inactive
  414.    group AttackRules
  415. {
  416.    xsEnableRule("attackGenerator2");
  417.    xsDisableSelf();
  418. }
  419.  
  420. //==============================================================================
  421. // Tech Researching Rules - all activated upon arrival at temple.
  422. //==============================================================================
  423. // Medium Infantry five minutes after arrival
  424. rule researchMediumInfantry
  425.    minInterval 300
  426.    inactive
  427. {
  428.     int planID=aiPlanCreate("Researching Medium Infantry.", cPlanResearch);
  429.     
  430.    if (planID < 0)
  431.       return;
  432.  
  433.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumInfantry);
  434.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy);
  435.  
  436.     aiPlanSetActive(planID);
  437.     aiEcho("Researching medium cavalry.");
  438.    
  439.     //Done.
  440.    xsDisableSelf();
  441. }
  442.  
  443. // Medium Cavalry seven minutes after arrival
  444. rule researchMediumCavalry
  445.    minInterval 420
  446.    inactive
  447. {
  448.     int planID=aiPlanCreate("Researching Medium Cavalry.", cPlanResearch);
  449.     
  450.    if (planID < 0)
  451.       return;
  452.  
  453.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumCavalry);
  454.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeStable);
  455.  
  456.     aiPlanSetActive(planID);
  457.     aiEcho("Researching medium cavalry.");
  458.    
  459.     //Done.
  460.    xsDisableSelf();
  461. }
  462.  
  463. // Medium Archers seven minutes after arrival
  464. rule researchMediumArchers
  465.    minInterval 420
  466.    inactive
  467. {
  468.     int planID=aiPlanCreate("Researching Medium Archers.", cPlanResearch);
  469.     
  470.    if (planID < 0)
  471.       return;
  472.  
  473.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumArchers);
  474.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArcheryRange);
  475.  
  476.     aiPlanSetActive(planID);
  477.     aiEcho("Researching medium cavalry.");
  478.    
  479.     //Done.
  480.    xsDisableSelf();
  481. }
  482.  
  483. // Heavy Infantry eight minutes after arrival.
  484. rule researchHeavyInfantry
  485.    minInterval 480
  486.    inactive
  487. {
  488.     int planID=aiPlanCreate("Researching Heavy Infantry.", cPlanResearch);
  489.     
  490.    if (planID < 0)
  491.       return;
  492.  
  493.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry);
  494.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy);
  495.  
  496.     aiPlanSetActive(planID);
  497.     aiEcho("Researching heavy infantry.");
  498.    
  499.     //Done.
  500.    xsDisableSelf();
  501. }
  502.  
  503. // Bronze weapons eight minutes after arrival.
  504. rule researchBronzeWeapons
  505.    minInterval 480
  506.    active
  507. {
  508.    int planID=aiPlanCreate("Bronze weapons research", cPlanResearch);
  509.    if (planID < 0)
  510.       return;
  511.  
  512.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeWeapons);
  513.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  514.    aiPlanSetActive(planID);
  515.    
  516.     //Done.
  517.    xsDisableSelf();
  518. }
  519.  
  520. // Heavy Archers nine minutes after arrival.
  521. rule researchHeavyArchers
  522.    minInterval 540
  523.    inactive
  524. {
  525.     int planID=aiPlanCreate("Researching Heavy Infantry.", cPlanResearch);
  526.     
  527.    if (planID < 0)
  528.       return;
  529.  
  530.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry);
  531.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArcheryRange);
  532.  
  533.     aiPlanSetActive(planID);
  534.     aiEcho("Researching heavy archers.");
  535.    
  536.     //Done.
  537.    xsDisableSelf();
  538. }
  539.  
  540. // Bronze shields ten minutes after arrival
  541. rule researchBronzeShields
  542.    minInterval 600
  543.    inactive
  544. {
  545.    int planID=aiPlanCreate("Bronze shields research", cPlanResearch);
  546.    if (planID < 0)
  547.       return;
  548.  
  549.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBronzeShields);
  550.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory);
  551.    aiPlanSetActive(planID);
  552.    
  553.     //Done.
  554.    xsDisableSelf();
  555. }
  556.  
  557. // Watchtower research ten minutes after arrival.
  558. rule researchWatchtower
  559.    minInterval 600
  560.    inactive
  561. {
  562.    int planID=aiPlanCreate("Watchtower research", cPlanResearch);
  563.    if (planID < 0)
  564.       return;
  565.  
  566.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechWatchTower);
  567.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeTower);
  568.    aiPlanSetActive(planID);
  569.    
  570.     //Done.
  571.    xsDisableSelf();
  572. }
  573.  
  574. // Boiling Oil research, twelve minutes after arrival
  575. rule researchBoilingOil
  576.    minInterval 720
  577.    inactive
  578. {
  579.    int planID=aiPlanCreate("Boiling Oil research", cPlanResearch);
  580.    if (planID < 0)
  581.       return;
  582.  
  583.     aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechBoilingOil);
  584.     aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeTower);
  585.    aiPlanSetActive(planID);
  586.    
  587.     //Done.
  588.    xsDisableSelf();
  589. }
  590.  
  591. // This rule helps bulletproof the "all my pigs get slaughtered en route" bug.
  592. // It ups response distance 60 seconds after Arkantos gets to the temple.  
  593. //
  594. // At this point, if the pigs are still trailing miles behind, the player deserves
  595. // to lose all his piggies, and if he does, I will point and laugh.
  596. rule responseDistanceSet
  597.    minInterval 60
  598.    inactive
  599. {
  600.     aiSetAttackResponseDistance(40.0);
  601.     xsDisableSelf();
  602. }
  603.  
  604. //=====================================================================================
  605. // Attack (and research) Enabler.  This is called from the scenario with an AI Func effect
  606. // when Arkantos reaches the Zeus temple.
  607. //=====================================================================================
  608. void attackEnabler(int scriptCall = -1)
  609. {
  610.     // Get the difficulty level.
  611.     difflevel=aiGetWorldDifficulty();
  612.  
  613.     aiEcho("*** AI: Arkantos has reached the Temple of Zeus. ***");
  614.     aiEcho("*** Attacks and research now enabled. ***");
  615.  
  616.     xsEnableRule("attack1Enabler");
  617.     xsEnableRule("attack2Enabler");
  618.     xsEnableRule("researchWatchtower");
  619.     xsEnableRule("responseDistanceSet");
  620.  
  621.     if ( difflevel > 0 )
  622.     {
  623.         xsEnableRule("researchMediumInfantry");
  624.         xsEnableRule("researchMediumArchers");
  625.         xsEnableRule("researchBronzeWeapons");
  626.         xsEnableRule("researchHeavyInfantry");
  627.         xsEnableRule("researchBoilingOil");
  628.     }
  629.  
  630.     if ( difflevel > 1 )
  631.     {
  632.         xsEnableRule("researchMediumCavalry");
  633.         xsEnableRule("researchBronzeShields");
  634.         xsEnableRule("researchHeavyArchers");
  635.     }
  636. }
  637.  
  638. //==============================================================================
  639. // MAIN.
  640. //==============================================================================
  641. void main(void)
  642. {
  643.     // Get the difficulty level.
  644.     difflevel=aiGetWorldDifficulty();
  645.  
  646.    //Startup.
  647.    miscStartup();
  648.  
  649.     if ( difflevel == 0 )
  650.     {
  651.         xsSetRuleMinInterval( "attack1Enabler", 240 );
  652.         xsSetRuleMinInterval( "attack2Enabler", 360 );
  653.  
  654.         xsSetRuleMinInterval( "attack1Generator", 210 );
  655.         xsSetRuleMinInterval( "attack2Generator", 300 );
  656.     }
  657.  
  658.     if ( difflevel > 0 )
  659.     {
  660.         attackMinimumGroupSize=5;
  661.         attackMaximumGroupSize=7;
  662.     }
  663.  
  664.     if ( difflevel == 3 )
  665.     {
  666.         attackMinimumGroupSize=8;
  667.         attackMaximumGroupSize=10;
  668.     }
  669.  
  670.    //Share the number to maintain.
  671.    int numberToMaintain=attackMinimumGroupSize*2;
  672.  
  673.    //Share a common gather point.
  674.    vector gatherPointSouth=kbGetBlockPosition("20871");
  675.     vector gatherPointNorth=kbGetBlockPosition("20873");
  676.  
  677.     vector gatherPointSiege=kbGetBlockPosition("22462");
  678.  
  679.    // Maintain X hypaspists.
  680.    int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  681.    if (maintainPlan1ID >= 0)
  682.    {
  683.         //Must set the type of unit to train.
  684.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  685.       //Set the number of units to maintain in the world at one time.
  686.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  687.       //Don't train units faster than every 30 seconds
  688.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 30);
  689.       //Set a gather point.
  690.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointSouth);
  691.       //Activate the plan.
  692.       aiPlanSetActive(maintainPlan1ID);
  693.    }
  694.  
  695.     // Maintain X hippikons.
  696.    int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  697.    if (maintainPlan2ID >= 0)
  698.    {
  699.         //Must set the type of unit to train.
  700.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  701.       //Set the number of units to maintain in the world at one time.
  702.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  703.       //Don't train units faster than every 45 seconds
  704.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 45);
  705.       //Set a gather point.
  706.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointNorth);
  707.       //Activate the plan.
  708.       aiPlanSetActive(maintainPlan2ID);
  709.    }
  710.  
  711.     // Maintain 4 toxotes.
  712.    int maintainPlan3ID=aiPlanCreate("Maintain 4 "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  713.    if (maintainPlan3ID >= 0)
  714.    {
  715.         //Must set the type of unit to train.
  716.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3);
  717.       //Set the number of units to maintain in the world at one time.
  718.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  719.       //Don't train units faster than every 50 seconds
  720.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 50);
  721.       //Set a gather point.
  722.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointSouth);
  723.       //Activate the plan.
  724.       aiPlanSetActive(maintainPlan3ID);
  725.    }
  726.  
  727.     // On higher difficulty levels, build siege equipment.
  728.     if ( difflevel > 1 )
  729.     {
  730.         // Maintain 2 petroboli.
  731.         int maintainPlan4ID=aiPlanCreate("Maintain 2 "+kbGetProtoUnitName(siegeUnitTypeID1), cPlanTrain);
  732.         if (maintainPlan4ID >= 0)
  733.         {
  734.             //Must set the type of unit to train.
  735.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, siegeUnitTypeID1);
  736.             //Total of 12 - if the player survives this, the AI stops makin' 'em.
  737.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToTrain, 0, 12);
  738.             //Set the number of units to maintain in the world at one time.
  739.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, 2);
  740.             //Don't train units faster than every 90 seconds 
  741.             aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 90);
  742.             //Set a gather point.
  743.             aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointSiege);
  744.             //Activate the plan.
  745.             aiPlanSetActive(maintainPlan4ID);
  746.         }
  747.     }
  748. }
  749.